// Shared functions for drawing
//
// PLAN: 1. These can be implemented by assembly for speed. -OB

#include "DrawShared.h"

// Set buffer to a specified color
// NOTE: 1. assume dst is 2 bytes aligned.
//		 2. count is in pixels.
void memset_color(UInt8 *dst, UInt32 colors, int count)
{
#ifdef __RGB16
    int pair;

    if ((UInt32) dst & 0x3)	// but is not 4 aligned
	{
	    // copy 1 pixel to destination
        *(UInt16*) dst = (UInt16) colors;

		// move to next pixel
        dst += 2; 
		count--;
    }
	
	pair = count >> 1;		// now how many 2 pixels

    while (pair--)
    {
        // copy 2 pixels to destination
        *(UInt32*) dst = colors;

		// move to next 2 pixel
        dst += 4;   
    }
    if (count & 1)
    {
        // copy 1 pixel to destination
        *(UInt16*) dst = (UInt16) colors;
    }
#else
    while (count--)
    {
		// copy 1 pixel to destination
		WRITE_PIXEL_PTR(dst, colors);

		// move to next pixel
        dst += PIXEL_SIZE;
    }
/*  or 
    unsigned char *p;
    unsigned char *end;
    UInt32 ldata;
	UInt16 sdata;
	int i;

	unsigned char a = (unsigned char)(data>>16);
	unsigned char b = (unsigned char)(data>>8);
	unsigned char c = (unsigned char) data;

	len = len * 3;

    if ((UInt32)dest & 0x01)
	{
        *dest = a;
        dest++;
		len--;
		ldata = (a | (c << 8) | (b << 16) | (a << 24));
		sdata = (c | b << 8);
    }
	else
	{
		ldata = (a | (c << 8) | (b << 16) | (a << 24));
		sdata = (c | b << 8);
	}

	p = dest;
    for (i = 0; i < len / 3; i++)
	{
        *(UInt32*) p = ldata;
		*(UInt16*) (p+4) = sdata;
		p += 6;
	}
} */
#endif
}

// Darken is RGBnew = RGBsrc / 2 + RGBdst / 2, i.e. blend(50, 50)
// NOTE: 1. assume dst is 2 bytes aligned.
//		 2. count is in pixels.
void memset_darken(UInt8 *dst, UInt32 colors, int count)
{
#if defined __RGB16
	UInt32 d;
    int pair;

    colors = (colors >> 1) & CLIP_BITMASK;
	
    if ((UInt32) dst & 0x3)	// but is not 4 aligned
	{
		// read 1 dst pixel
        d = *(UInt16*) dst; 

        // write result to destination
        *(UInt16*) dst = (UInt16) (colors + ((d >> 1) & CLIP_BITMASK));

		// move to next pixel
        dst += 2; 

		count--;
    }	

	pair = count >> 1;		// how many 2 pixels

    while (pair--)
    {
		// read 2 dst pixel
        d = *(UInt32*) dst;  

        // write result to destination
        *(UInt32*) dst = colors + ((d >> 1) & CLIP_BITMASK);

		// move to next 2 pixel
        dst += 4;   
    } 
    if (count & 1)
    {
		// read 1 dst pixel
        d = *(UInt16*) dst; 

        // write result to destination
        *(UInt16*) dst = (UInt16) (colors + ((d >> 1) & CLIP_BITMASK));
    }
#elif defined __RGB24
	UInt8 bi1, bi2, bi3;

#ifdef _LITTLE_ENDIAN
	bi1 = ((UInt8) colors) >> 1;
	bi2 = (UInt8) (((UInt16) colors) >> 9);
	bi3 = (UInt8) (colors >> 17);
#else
	bi1 = (UInt8) (colors >> 17);	
	bi2 = (UInt8) (((UInt16) colors) >> 9);
	bi3 = ((UInt8) colors) >> 1;
#endif

    while (count--)
    {
		// write result to destination
		// and move to next byte
		*dst = (*dst >> 1) + bi1;	dst++;
		*dst = (*dst >> 1) + bi2;	dst++;
		*dst = (*dst >> 1) + bi3;	dst++;
    }
#endif
}

// NOTE: count is in pixels.
void memcpy_darken(UInt8 *dst, const UInt8 *src, int count)
{
#if defined __RGB16
	UInt16 d, s;

    while (count--)
    {
		// read 1 src pixel
        s = *(UInt16*) src;    

		// read 1 dst pixel
        d = *(UInt16*) dst;    

        // write result to destination
        *(UInt16*) dst = ((s >> 1) & CLIP_BITMASK) + ((d >> 1) & CLIP_BITMASK);

		// move to next pixel
        dst += 2;   
        src += 2;
    } 
#elif defined __RGB24
	count *= 3;		// total bytes 

    while (count--)
    {
		// write result to destination
		*dst = (*dst >> 1) + (*src >> 1);	
		
		// move to next (r/g/b) byte
		dst++; src++;
    }
#endif
}

// Blend is RGBnew = RGBsrc * sf/100 + RGBdst * df/100
// NOTE: 1. sf,df is 0 ~ 100.
//		 2. count is in pixels.
void memset_blend(UInt8 *dst, UInt32 color, int count, int sf, int df)
{
#if defined __RGB16
    UInt32 d;
    UInt32 r, g, b;
    UInt32 mix1, mix2, mix3;

    r = (color & R_BITMASK) * sf;
    g = (color & G_BITMASK) * sf;
    b = (color & B_BITMASK) * sf;

    while (count--)
    {
        // read 1 dst pixel
        d = *(UInt16*) dst;

        // mix colors by blending
        mix1 = ((d & R_BITMASK) * df + r) / 100;
        mix2 = ((d & G_BITMASK) * df + g) / 100;
        mix3 = ((d & B_BITMASK) * df + b) / 100;

        // test overflow
        if (mix1 > R_BITMASK) mix1 = R_BITMASK;
        if (mix2 > G_BITMASK) mix2 = G_BITMASK;
        if (mix3 > B_BITMASK) mix3 = B_BITMASK;

        // clip underflow
        mix1 &= R_BITMASK;
        mix2 &= G_BITMASK;

        // write result to destination
        *(UInt16*) dst = (UInt16) (mix1 | mix2 | mix3);

        // move to next pixel
        dst += 2;
    }
#elif defined __RGB24 
	UInt32 dw1, dw2, dw3;
    UInt32 mix;

#ifdef _LITTLE_ENDIAN
	dw1 = ((UInt8) color) * sf;
	dw2 = (((UInt16) color) >> 8) * sf;
	dw3 = (color >> 16) * sf;
#else //  _BIG_ENDIAN
	dw1 = (color >> 16) * sf;	
	dw2 = (((UInt16) color) >> 8) * sf;
	dw3 = ((UInt8) color) * sf;
#endif

    while (count--)
    {
		// byte 1
		mix = ((UInt32) (*dst) * df + dw1) / 100; 
		if (mix > 255) mix = 255;		// test overflow
		*dst = (UInt8) mix;	 dst++;		// write and move to next byte

		// byte 2
		mix = ((UInt32) (*dst) * df + dw2) / 100; 
		if (mix > 255) mix = 255;
		*dst = (UInt8) mix;  dst++; 

		// byte 3
		mix = ((UInt32) (*dst) * df + dw3) / 100; 
		if (mix > 255) mix = 255;
		*dst = (UInt8) mix;  dst++; 
    }
#endif
}

// NOTE: count is in pixels.
void memcpy_blend(UInt8 *dst, const UInt8 *src, int count, int sf, int df)
{
#if defined __RGB16
	UInt32 d, s;
    UInt32 mix1, mix2, mix3;

    while (count--)
    {
        // read 1 src pixel
        s = *(UInt16*) src;

        // read 1 dst pixel
        d = *(UInt16*) dst;

        // mix colors by blending
        mix1 = ((d & R_BITMASK) * df + (s & R_BITMASK) * sf) / 100;
        mix2 = ((d & G_BITMASK) * df + (s & G_BITMASK) * sf) / 100;
        mix3 = ((d & B_BITMASK) * df + (s & B_BITMASK) * sf) / 100;

        // test overflow and clip underflow
        if (mix1 > R_BITMASK) mix1  = R_BITMASK;
        else                  mix1 &= R_BITMASK;
        if (mix2 > G_BITMASK) mix2  = G_BITMASK;
        else                  mix2 &= G_BITMASK;
        if (mix3 > B_BITMASK) mix3  = B_BITMASK;

        // write result to destination
        *(UInt16*) dst = (UInt16) (mix1 | mix2 | mix3);

        // move to next pixel
        dst += 2;
        src += 2;
    }
#elif defined __RGB24
    UInt32 mix;

	count *= 3;		// total bytes

    while (count--)
    {
		// mix colors by blending
		mix = ((UInt32) (*dst) * df + (UInt32) (*src) * sf) / 100; 

		// test overflow
		if (mix > 255) mix = 255;

		// write result to destination
		*dst = (UInt8) mix;	
		
		// move to next (r/g/b) byte
		dst++; src++;
    }
#endif
}

// Alpha is  RGBnew = RGBsrc * alpha/255 + RGBdst * (255-alpha)/255
//                  = (RGBsrc * alpha + RGBdst * (255 - alpha)) / 255
// NOTE: 1. alpha is 0 ~ 255.
//		 2. count is in pixels.
void memset_alpha(UInt8 *dst, UInt32 color, int count, UInt8 alpha)
{
#if defined __RGB16
    UInt32 d;
    UInt32 bits;
    UInt32 mix1, mix2, mix3;

    while (count--)
    {
        if (alpha == 0xFF)		// if alpha is 255, we don't need any blending
        {
			// copy 1 pixel to destination
			*(UInt16*) dst = (UInt16) color;
        }
		else if (alpha == 0x7F)	// if alpha is 128, just half blending
		{
			// read 1 dst pixel
			d = *(UInt16*) dst; 

			// write result to destination
			*(UInt16*) dst = (UInt16) (((color >> 1) & CLIP_BITMASK) + ((d >> 1) & CLIP_BITMASK));
		}
        else if (alpha)			// if alpha is above 0, we draw the pixel        
        {
			// read 1 dst pixel
			d = *(UInt16*) dst;

			bits = (d & R_BITMASK);
			mix1 = bits + (((((color & R_BITMASK) - bits) * alpha) >> 8) & R_BITMASK);

			bits = (d & G_BITMASK);
			mix2 = bits + (((((color & G_BITMASK) - bits) * alpha) >> 8) & G_BITMASK);

			bits = (d & B_BITMASK);
			mix3 = bits + (((((color & B_BITMASK) - bits) * alpha) >> 8) & B_BITMASK);

			// write result to destination
			*(UInt16*) dst = (UInt16) (mix1 | mix2 | mix3);
        }

        // move to next pixel
        dst += 2;
    }
#elif defined __RGB24
	UInt32 dw1, dw2, dw3;
	UInt8  ca;

	if (alpha == 0xFF)
	{
#ifdef _LITTLE_ENDIAN
		dw1 = (UInt8)(color);
		dw2 = (UInt8)(color >> 8);
		dw3 = (UInt8)(color >> 16);
#else //  _BIG_ENDIAN
		dw1 = (UInt8)(color >> 16);
		dw2 = (UInt8)(color >> 8);
		dw3 = (UInt8)(color);
#endif
	}
	else
	{
		ca = 255 - alpha;

#ifdef _LITTLE_ENDIAN
		dw1 = ((UInt8) color) * alpha;
		dw2 = (((UInt16) color) >> 8) * alpha;
		dw3 = (color >> 16) * alpha;
#else //  _BIG_ENDIAN
		dw1 = (color >> 16) * alpha;	
		dw2 = (((UInt16) color) >> 8) * alpha;
		dw3 = ((UInt8) color) * alpha;
#endif
	}

    while (count--)
    {
		if (alpha == 0xFF)		// if alpha is 255, we don't need any blending
		{
			// copy pixel to destination
			*dst = (UInt8) dw1;	 dst++;
			*dst = (UInt8) dw2;	 dst++;
			*dst = (UInt8) dw3;	 dst++;
		}
		else if (alpha)			// if alpha is above 0, we draw the pixel        
		{
			// write result to destination
			*dst = (UInt8) (((UInt32) (*dst) * ca + dw1) >> 8);  dst++;
			*dst = (UInt8) (((UInt32) (*dst) * ca + dw2) >> 8);  dst++;
			*dst = (UInt8) (((UInt32) (*dst) * ca + dw3) >> 8);	 dst++;
/* or but may overflow?
			*dst = *dst + ((((color >> 16)			 - *dst) * alpha) >> 8); dst++;
			*dst = *dst + ((((((UInt16) color) >> 8) - *dst) * alpha) >> 8); dst++;
			*dst = *dst + (((((UInt8) color)		 - *dst) * alpha) >> 8); dst++;
*/
		}
	}
#endif
}

void memcpy_alpha(UInt8 *dst, const UInt8 *src, int count, UInt8 alpha)
{
#if defined __RGB16
    UInt32 d, s;
    UInt32 bits;
    UInt32 mix1, mix2, mix3;

    while (count--)
    {
        if (alpha == 0xFF)  // if alpha is 255, we don't need any blending
        {
			// copy 1 pixel to destination
            *(UInt16*) dst = *(UInt16*) src;
        }
        else if (alpha)     // if alpha is above 0, we draw the pixel        
        {
			// read 1 src pixel
            s = *(UInt16*) src; 

			// read 1 dst pixel
            d = *(UInt16*) dst;    

            // mix colors by alpha blending
            bits = (d & R_BITMASK);
            mix1 = bits + (((((s & R_BITMASK) - bits) * alpha) >> 8) & R_BITMASK);

            bits = (d & G_BITMASK);
            mix2 = bits + (((((s & G_BITMASK) - bits) * alpha) >> 8) & G_BITMASK);

            bits = (d & B_BITMASK);
            mix3 = bits + (((((s & B_BITMASK) - bits) * alpha) >> 8) & B_BITMASK);

            // write result to destination
            *(UInt16*) dst = (UInt16) (mix1 | mix2 | mix3);
        }

        // move to next pixel
        src += 2;
        dst += 2;
    }
#elif defined __RGB24
	UInt8 ca = 255 - alpha;

	count *= 3;				// total bytes

    while (count--)
    {
        if (alpha == 0xFF)  // if alpha is 255, we don't need any blending
        {
			// copy 1 byte to destination
            *dst = *src;
        }
        else if (alpha)     // if alpha is above 0, we draw the pixel        
        {
			// write result to destination
			*dst = (UInt8) (((UInt32) (*dst) * ca + (UInt32) (*src) * alpha) >> 8); 
/* or but may overflow?
			*dst = *dst + (((*src - *dst) * a) >> 8);
*/
		}

  		// move to next (r/g/b) byte
		dst++; src++;
    }	
#endif
}

void memcpy_alpha_array(UInt8 *dst, const UInt8 *src, int count, const UInt8 *alpha)
{
#if defined __RGB16
    UInt32 d, s, a;
    UInt32 bits, result, a8;
    UInt32 mix1, mix2, mix3;

    // processing 1 pixel first
    if (count & 1)
    {
		// read 1 alpha value
        a = *alpha;		

        if (a == 0xFF)  // if alpha is 255, we don't need any blending
        {
			// copy 1 pixel to destination
            *(UInt16*) dst = *(UInt16*) src;
        }
        else if (a)     // if alpha is above 0, we draw the pixel        
        {
			// read 1 src pixel
            s = *(UInt16*) src;  

			// read 1 dst pixel
            d = *(UInt16*) dst;  

            // mix colors by alpha blending
            bits = (d & R_BITMASK);
            mix1 = bits + (((((s & R_BITMASK) - bits) * a) >> 8) & R_BITMASK);

            bits = (d & G_BITMASK);
            mix2 = bits + (((((s & G_BITMASK) - bits) * a) >> 8) & G_BITMASK);

            bits = (d & B_BITMASK);
            mix3 = bits + (((((s & B_BITMASK) - bits) * a) >> 8) & B_BITMASK);

            // write result to destination
            *(UInt16*) dst = (UInt16) (mix1 | mix2 | mix3);
        }

        // move to next pixel
        src += 2;
        dst += 2;
        alpha++;
    }

    count = count / 2;      // how many 2 pixels

    // next processing 2 pixels at a time
    while (count--)
    {
		// read 2 alpha values
        a = *(UInt16*) alpha;      

        if (a == 0xFFFF)    // if alpha is 255, we don't need any blending
        {
			// copy 2 pixels to destination
            *(UInt32*) dst = *(UInt32*) src;
        }
        else if (a)         // if alpha is above 0, we draw the pixel        
        {
			// read 2 src pixels
            s = *(UInt32*) src;

			// read 2 dst pixels
            d = *(UInt32*) dst;     

            result = d;

            a8 = a & 0xFF;

			// deal low pixel
            if (a8 == 0xFF)
            {
                result = (result & 0xFFFF0000) | (s & 0xFFFF);
            }
            else if (a8)
            {
                // mix colors by alpha blending
                bits = (d & R_BITMASK);
                mix1 = bits + (((((s & R_BITMASK) - bits) * a8) >> 8) & R_BITMASK);

                bits = (d & G_BITMASK);
                mix2 = bits + (((((s & G_BITMASK) - bits) * a8) >> 8) & G_BITMASK);

                bits = (d & B_BITMASK);
                mix3 = bits + (((((s & B_BITMASK) - bits) * a8) >> 8) & B_BITMASK);

                result = result & 0xFFFF0000;

                result |= (mix1 | mix2 | mix3);
            }

			// deal high pixel
            a >>= 8;

            if (a == 0xFF)
            {
                result = (result & 0xFFFF) | (s & 0xFFFF0000);
            }
            else if (a)
            {
                s >>= 16;
                d >>= 16;

                // mix colors by alpha blending
                bits = (d & R_BITMASK);
                mix1 = bits + (((((s & R_BITMASK) - bits) * a) >> 8) & R_BITMASK);

                bits = (d & G_BITMASK);
                mix2 = bits + (((((s & G_BITMASK) - bits) * a) >> 8) & G_BITMASK);

                bits = (d & B_BITMASK);
                mix3 = bits + (((((s & B_BITMASK) - bits) * a) >> 8) & B_BITMASK);

                result = result & 0xFFFF;

                result |= ((mix1 | mix2 | mix3) << 16);
            }

			// write result to destination
            *(UInt32 *) dst = result;
        }

        // move to next 2 pixels
        dst   += 4;
        src   += 4;
        alpha += 2;
    }
#elif defined __RGB24
    UInt8 a;

	count *= 3;		// total bytes

    while (count--)
    {
		// read 1 alpha value
        a = *alpha;		

		// write result to destination
		*dst = (UInt8) (((UInt32) (*dst) * (255 - a) + (UInt32) (*src) * a) >> 8);
/* or but may overflow?
		*dst = *dst + (((*src - *dst) * a) >> 8);
*/
		// move to next (r/g/b) byte
		dst++; src++; alpha++;
    }	
#endif
}

// Alpha is  RGBnew = RGBsrc * a/255 + RGBdst * (255-a)/255, a is 0 ~ 255
//                  = (RGBsrc * a + RGBdst * (255 - a)) / 255
void write_pixel_alpha(UInt8 *dst, UInt32 color, BYTE alpha)
{
#if defined __RGB16
	UInt32 d;
    UInt32 bits;
    UInt32 mix1, mix2, mix3;

    if (alpha == 0xFF)		// if alpha is 255, we don't need any blending
    {
		// copy 1 pixel to destination
		*(UInt16*) dst = (UInt16) color;
    }
	else if (alpha == 0x7F)	// if alpha is 128, just half blending
	{
		// read 1 dst pixel
        d = *(UInt16*) dst; 

        // write result to destination
        *(UInt16*) dst = (UInt16) (((color >> 1) & CLIP_BITMASK) + ((d >> 1) & CLIP_BITMASK));
	}
    else if (alpha)			// if alpha is above 0, we draw the pixel        
    {
		// read 1 dst pixel
		d = *(UInt16*) dst;

		// mix colors by alpha blending
		bits = (d & R_BITMASK);
		mix1 = bits + (((((color & R_BITMASK) - bits) * alpha) >> 8) & R_BITMASK);

		bits = (d & G_BITMASK);
		mix2 = bits + (((((color & G_BITMASK) - bits) * alpha) >> 8) & G_BITMASK);

		bits = (d & B_BITMASK);
		mix3 = bits + (((((color & B_BITMASK) - bits) * alpha) >> 8) & B_BITMASK);

		// write result to destination
		*(UInt16*) dst = (UInt16) (mix1 | mix2 | mix3);
	}
#else
	UInt32 dw1, dw2, dw3;
	
    if (alpha == 0xFF)		// if alpha is 255, we don't need any blending
    {
		// copy 1 pixel to destination
		WRITE_PIXEL_PTR(dst, color);
    }
	else if (alpha == 0x7F)	// if alpha is 128, just half blending
	{
        // write result to destination
#ifdef _LITTLE_ENDIAN
        *(UInt8*) dst = (UInt8) ((((UInt8) color) >> 1) + (*dst >> 1));	 dst++;	// and move to next byte
        *(UInt8*) dst = (UInt8) ((((UInt16) color) >> 9) + (*dst >> 1)); dst++;
        *(UInt8*) dst = (UInt8) ((color >> 17) + (*dst >> 1));			 
#else // _BIG_ENDIAN
        *(UInt8*) dst = (UInt8) ((color >> 17) + (*dst >> 1));			 dst++;
        *(UInt8*) dst = (UInt8) ((((UInt16) color) >> 9) + (*dst >> 1)); dst++;
        *(UInt8*) dst = (UInt8) ((((UInt8) color) >> 1) + (*dst >> 1));	
#endif
	}
    else if (alpha)			// if alpha is above 0, we draw the pixel        
    {
#ifdef _LITTLE_ENDIAN
		dw1 = ((UInt8) color) * alpha;
		dw2 = (((UInt16) color) >> 8) * alpha;
		dw3 = (color >> 16) * alpha;	
#else // _BIG_ENDIAN
		dw1 = (color >> 16) * alpha;	
		dw2 = (((UInt16) color) >> 8) * alpha;
		dw3 = ((UInt8) color) * alpha;
#endif
        // write result to destination
		*dst = (UInt8) (((UInt32) (*dst) * (255 - alpha) + dw1) >> 8); 	 dst++;	// and move to next byte
		*dst = (UInt8) (((UInt32) (*dst) * (255 - alpha) + dw2) >> 8); 	 dst++;
		*dst = (UInt8) (((UInt32) (*dst) * (255 - alpha) + dw3) >> 8);	
/* or but may overflow?
		*dst = *dst + ((((color >> 16)			 - *dst) * alpha) >> 8); dst++;
		*dst = *dst + ((((((UInt16) color) >> 8) - *dst) * alpha) >> 8); dst++;
		*dst = *dst + (((((UInt8) color)		 - *dst) * alpha) >> 8);
*/
	}
#endif
}
